#include "A_Config.h"
#include "ESPNowConfig.h"
#include "esp_now.h"

ESPNowHelper ESPNow;

#define SOH 0x01
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define SYN 0x16
#define CAN 0x18

static void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt)
{
    if (((const espNowPkt *)data)->deviceID_To != ESPNow.myDeviceID && ((const espNowPkt *)data)->deviceID_To != 0xffff)
        return;
    if (len >= 4)
    {
        if (ESPNow.RecvCB)
            ESPNow.RecvCB(((const espNowPkt *)data)->payload, len - 4, ((const espNowPkt *)data)->deviceID_From);
    }
    if (replyPrt && ((const espNowPkt *)data)->deviceID_To == ESPNow.myDeviceID)
    {
        espNowPkt re;
        re.deviceID_From = ESPNow.myDeviceID;
        re.deviceID_To = ((const espNowPkt *)data)->deviceID_From;
        re.payload[0] = ACK;
        espNowFloodingMesh_sendReply((uint8_t *)&re, 5, 3 /*ttl*/, replyPrt);
    }
}

void ESPNowHelper::init()
{
    espNowFloodingMesh_secredkey(secredKey);
    espNowFloodingMesh_setAesInitializationVector(iv);
    espNowFloodingMesh_setToMasterRole(false);
    espNowFloodingMesh_setToBatteryNode();
    espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv);
    RecvCB = NULL;
}

void ESPNowHelper::start(uint8_t channel)
{
    espNowFloodingMesh_begin(channel, bsid);
    _isStarted = true;
}

void ESPNowHelper::end()
{
    WiFi.mode(WIFI_OFF);
    if (_isStarted)
    {
        esp_now_deinit();
        _isStarted = false;
    }
}

void ESPNowHelper::update()
{
    espNowFloodingMesh_loop(); //这个函数主要是用来清理数据库的
}

void ESPNowHelper::reqTimeSync(uint32_t timeout, uint8_t count)
{
    espNowFloodingMesh_syncTimeAndWait(timeout, count);
}
void ESPNowHelper::sendTo(uint8_t *pkt, uint8_t size, uint16_t deviceID)
{
    espNowPkt p;
    if (size > 232)
        return;
    p.deviceID_From = myDeviceID;
    p.deviceID_To = deviceID;
    espNowFloodingMesh_send((uint8_t *)&p, size + 4, 3); // set ttl to 3
}

bool ESPNowHelper::sendAndWaitAck(uint8_t *pkt, uint8_t size, uint16_t deviceID, uint32_t timeout, uint32_t retry)
{
    espNowPkt p;
    if (size > 232)
        return false;
    p.deviceID_From = myDeviceID;
    p.deviceID_To = deviceID;
    memcpy(p.payload, pkt, size);
    return espNowFloodingMesh_sendAndWaitReply((uint8_t *)&p, size + 4, 3, retry, NULL, timeout);
}

/**
 * ESPNow文件传输协议
 * 1、发送方发送TimeSync，接收方收到后同步自己的RTC
 * 2、接收方发送1字节SYN表示开始传输，注意这个数据包发送时设置packet_pos=0，收到后接收方设置packet_pos=0，发送方设置packet_pos=1
 * 3、发送方收到后，开始发送文件，接收方收到后回复ACK。接收方随时可发送CAN取消文件传输
 *    包内为数据包序号+文件内容，接收方收到后自动递增地址，如果收到len=0的数据包视作文件传输完成
 * 4、传输完毕后，双方可直接结束
 */
///////////////////////////////////////////////////////
static uint32_t _packet_pos;
static File *_f = NULL;
static uint8_t *buffer;
static uint8_t *buffer_curr;
static bool finished = false;
static uint16_t _deviceID = 0;
static size_t totalSize = 0;
static uint32_t last_pkt_time;
static void File_RECV_CB(const uint8_t *pkt, uint8_t size, uint16_t from)
{
    uint32_t pos_rec;
    if (from != _deviceID)
        return;
    if (size < 4)
        return; //长度错误的数据包
    size -= 4;
    memcpy(&pos_rec, pkt, 4);
    if (size == 0)
    {
        finished = true;
        return;
    }
    else if (_packet_pos >= pos_rec)
    {
        return;
    }
    else
    {
        totalSize += size;
        if (buffer)
        {
            //存储到buffer
            memcpy(buffer_curr, pkt + 4, size);
            buffer_curr += size;
        }
        else if (_f)
        {
            _f->write(pkt + 4, size);
        }
        _packet_pos++;
    }
    last_pkt_time = millis();
}

static bool receivedSYN = false;
static void File_SEND_CB(const uint8_t *pkt, uint8_t size, uint16_t from)
{
    if (from != _deviceID)
        return;
    if (size == 5 && pkt[4] == SYN)
        receivedSYN = true;
}

bool ESPNowHelper::_recvFile(uint32_t timeout)
{
    _packet_pos = 0;
    totalSize = 0;
    finished = false;
    ESPNow.RecvCB = File_RECV_CB;
    bool b = ESPNow.isStarted();
    if (b == false)
        ESPNow.start();
    espNowFloodingMesh_syncTimeAndWait(timeout / 10, 10);

    uint8_t pkt[5];
    memcpy(pkt, &_packet_pos, 4);
    pkt[4] = SYN;
    _packet_pos = 0;
    bool suc = sendAndWaitAck((uint8_t *)&pkt, 5, _deviceID);
    if (!suc)
        goto end;
    last_pkt_time = millis();
    while (finished == false)
    {
        vTaskDelay(100);
        if (millis() - last_pkt_time > timeout)
            break;
    }
end:
    ESPNow.RecvCB = NULL;
    if (b == false)
        ESPNow.end();
    return finished;
}

bool ESPNowHelper::recvFile(File *f, uint16_t deviceID, uint32_t timeout)
{
    _f = f;
    buffer = NULL;
    buffer_curr = NULL;
    _deviceID = deviceID;
    return _recvFile(timeout);
}

bool ESPNowHelper::recvFile(uint8_t *buf, uint16_t deviceID, uint32_t timeout)
{
    _f = NULL;
    buffer = buf;
    buffer_curr = buf;
    _deviceID = deviceID;
    return _recvFile(timeout);
}

bool ESPNowHelper::_sendFile(uint32_t timeout)
{
    uint8_t pkt[255];
    finished = false;
    bool b = ESPNow.isStarted();
    if (b == false)
        ESPNow.start();
    espNowFloodingMesh_setSyncronized();
    receivedSYN = false;
    ESPNow.RecvCB = File_SEND_CB;
    uint32_t last_millis = millis();
    while (receivedSYN == false)
    {
        vTaskDelay(50);
        if (millis() - last_millis > timeout)
            break;
    }
    ESPNow.RecvCB = NULL;
    //收到同步消息，开始传输
    if (receivedSYN)
    {
        uint8_t size;
        _packet_pos = 1;
        if (_f)
        {
            while (1)
            {
                size = _f->readBytes((char *)(pkt + 4), ESPNOW_FILE_LEN_PER_PKT);
                memcpy(pkt, &_packet_pos, 4);
                bool suc = sendAndWaitAck((uint8_t *)&pkt, size + 4, _deviceID);
                if (suc == false)
                    break;
                if (size == 0)
                {
                    finished = true;
                    break;
                }
                ++_packet_pos;
            }
        }
        else
        {
            uint32_t current_pos = 0;
            while (1)
            {
                if (totalSize - current_pos > ESPNOW_FILE_LEN_PER_PKT)
                    size = ESPNOW_FILE_LEN_PER_PKT;
                else
                    size = totalSize - current_pos;
                memcpy(pkt + 4, buffer + current_pos, size);
                memcpy(pkt, &_packet_pos, 4);
                bool suc = sendAndWaitAck((uint8_t *)&pkt, size + 4, _deviceID);
                if (suc == false)
                    break;
                if (size == 0)
                {
                    finished = true;
                    break;
                }
                current_pos += size;
                ++_packet_pos;
            }
        }
    }
    if (b == false)
        ESPNow.end();
    return finished;
}

bool ESPNowHelper::sendFile(File *f, uint16_t deviceID, uint32_t timeout)
{
    _f = f;
    buffer = NULL;
    buffer_curr = NULL;
    _deviceID = deviceID;
    return _sendFile(timeout);
}

bool ESPNowHelper::sendFile(uint8_t *buf, size_t size, uint16_t deviceID, uint32_t timeout)
{
    _f = NULL;
    buffer = buf;
    buffer_curr = buf;
    _deviceID = deviceID;
    return _sendFile(timeout);
}

bool ESPNowHelper::sendCmd(enum_server_cmd_t cmd, const uint8_t *data, uint8_t size, uint16_t deviceID, bool syncTime)
{
    uint8_t pkt[255];
    pkt[0] = cmd;
    memcpy(pkt + 1, data, size);
    if (syncTime)
        reqTimeSync(100, 5);
    return sendAndWaitAck(pkt, size + 1, deviceID);
}

size_t ESPNowHelper::getLastFileSize()
{
    return totalSize;
}